﻿using System.Windows;
using System.Windows.Interactivity;
using System.Windows.Controls;
using System;
using System.Windows.Shapes;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace GameLogic.Dev
{
    public class MoveObjectOnPathBehavior : Behavior<FrameworkElement>
    {
        Canvas LayoutRoot;

        protected override void OnAttached()
        {
            base.OnAttached();

            this.AssociatedObject.Loaded += (s1, e1) =>
                {

                    // Check that parent object is in canvas
                    if (!(this.AssociatedObject.Parent is Canvas))
                    {
                        throw new Exception("Parent of associated object must be canvas.");
                    }

                    LayoutRoot = this.AssociatedObject.Parent as Canvas;

                    this.AssociatedObject.MouseLeftButtonDown += (s, e) =>
                        {
                            this.AssociatedObject.CaptureMouse();
                            StartDrawing(e.GetPosition(LayoutRoot));
                        };

                    this.AssociatedObject.MouseMove += (s, e) =>
                        {
                            if (_isDrawing)
                            {
                                AddPoint(e.GetPosition(LayoutRoot));
                            }
                        };

                    this.AssociatedObject.MouseLeftButtonUp += (s, e) =>
                        {
                            if (this.HidePathAfterMouseUp)
                            {
                                LayoutRoot.Children.Remove(_currentPath);
                            }
                            this.AssociatedObject.ReleaseMouseCapture();
                            StopDrawing();
                        };
                };
        }

        public bool HidePathAfterMouseUp
        {
            get { return (bool)GetValue(HidePathAfterMouseUpProperty); }
            set { SetValue(HidePathAfterMouseUpProperty, value); }
        }

        public static readonly DependencyProperty HidePathAfterMouseUpProperty =
            DependencyProperty.Register(
                "HidePathAfterMouseUp", 
                typeof(bool), 
                typeof(MoveObjectOnPathBehavior), 
                new PropertyMetadata(true));

        bool _isDrawing = false;
        Point? _lastPoint;

        Path _currentPath;
        PathGeometry _pathGeometry;
        PathFigure _pathFigure;
        PolyLineSegment _polyLineSegment;

        private void StartDrawing(Point point)
        {
            _currentPath = new Path();
            _currentPath.StrokeThickness = 5;
            _currentPath.Stroke = new SolidColorBrush(Colors.White);
            _currentPath.StrokeDashArray = new DoubleCollection() { 2.0, 1.0 };

            _pathGeometry = new PathGeometry();
            _currentPath.Data = _pathGeometry;

            LayoutRoot.Children.Add(_currentPath);

            _pathFigure = new PathFigure();
            _pathFigure.StartPoint = point;
            _lastPoint = point;
            _pathGeometry.Figures.Add(_pathFigure);

            _isDrawing = true;
        }

        private void OnPathDrawn()
        {
            MoveObject(_currentPath, this.AssociatedObject, this.PixelPerSecond);
        }

        public double PixelPerSecond
        {
            get { return (double)GetValue(PixelPerSecondProperty); }
            set { SetValue(PixelPerSecondProperty, value); }
        }

        public static readonly DependencyProperty PixelPerSecondProperty =
            DependencyProperty.Register(
                "PixelPerSecond", 
                typeof(double), 
                typeof(MoveObjectOnPathBehavior), 
                new PropertyMetadata(100.0));
            
        private void MoveObject(Path path, FrameworkElement element, double pixelPerSecond)
        {
            element.VerticalAlignment = System.Windows.VerticalAlignment.Top;
            element.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;

            TransformGroup tg = new TransformGroup();
            
            // Prepare for rotation
            RotateTransform rt = new RotateTransform();
            rt.CenterX = element.ActualWidth / 2;
            rt.CenterY = element.ActualHeight / 2;
            tg.Children.Add(rt);

            // Center Object
            TranslateTransform tt = new TranslateTransform();
            tt.X = -element.ActualWidth / 2;
            tt.Y = -element.ActualHeight / 2;
            tg.Children.Add(tt);

            element.RenderTransform = tg;

            if (element.Resources.Contains("MyAnimation"))
            {
                element.Resources.Remove("MyAnimation");
            }

            double pathLength = PathHelper.GetPathLength(path);
            double seconds = pathLength / pixelPerSecond;
            int count = (int)seconds * 100;

            if (element.Name == string.Empty)
            {
                element.Name = "Dummy" + Guid.NewGuid().ToString();
            }

            Storyboard sb = new Storyboard();

            DoubleAnimationUsingKeyFrames daX = new DoubleAnimationUsingKeyFrames();
            Storyboard.SetTargetProperty(daX, new PropertyPath("(Canvas.Left)"));
            Storyboard.SetTargetName(daX, element.Name);
            sb.Children.Add(daX);

            DoubleAnimationUsingKeyFrames daY = new DoubleAnimationUsingKeyFrames();
            Storyboard.SetTargetProperty(daY, new PropertyPath("(Canvas.Top)"));
            Storyboard.SetTargetName(daY, element.Name);
            sb.Children.Add(daY);

            DoubleAnimationUsingKeyFrames daR = new DoubleAnimationUsingKeyFrames();
            Storyboard.SetTargetProperty(daR, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(RotateTransform.Angle)"));
            Storyboard.SetTargetName(daR, element.Name);
            sb.Children.Add(daR);

            double result = 0.0;

            Point? _lastPoint = null;
            Point? _firstPoint = null;
            PathGeometry pathGeometry = path.Data as PathGeometry;
            foreach (var pf in pathGeometry.Figures)
            {
                foreach (var ps in pf.Segments)
                {
                    PolyLineSegment pls = ps as PolyLineSegment;

                    foreach (var p in pls.Points)
                    {
                        if (_firstPoint == null)
                        {
                            _firstPoint = p;
                            TimeSpan ts = new TimeSpan(0, 0, 0);

                            LinearDoubleKeyFrame kfX = new LinearDoubleKeyFrame();
                            kfX.KeyTime = ts;
                            kfX.Value = p.X;
                            daX.KeyFrames.Add(kfX);

                            LinearDoubleKeyFrame kfY = new LinearDoubleKeyFrame();
                            kfY.KeyTime = ts;
                            kfY.Value = p.Y;
                            daY.KeyFrames.Add(kfY);

                            //LinearDoubleKeyFrame kfR = new LinearDoubleKeyFrame();
                            //kfR.KeyTime = ts;
                            //kfR.Value = p.
                        }

                        if (_lastPoint != null)
                        {
                            // Calc diff between p1 and p2
                            double diff = PathHelper.Distance2D(p, _lastPoint.Value);

                            result += diff;

                            double diffInSeconds = (result / pathLength) * seconds;
                            int secondsInMilliseconds = (int)(diffInSeconds * 1000);

                            TimeSpan ts = new TimeSpan(0, 0, 0, 0, secondsInMilliseconds);

                            ExtendedPoint ep = PathHelper.GetPosition(path, result);
                            
                            LinearDoubleKeyFrame kfX = new LinearDoubleKeyFrame();
                            kfX.KeyTime = ts;
                            kfX.Value = p.X;
                            daX.KeyFrames.Add(kfX);

                            LinearDoubleKeyFrame kfY = new LinearDoubleKeyFrame();
                            kfY.KeyTime = ts;
                            kfY.Value = p.Y;
                            daY.KeyFrames.Add(kfY);

                            if (ep != null)
                            {
                                LinearDoubleKeyFrame kfR = new LinearDoubleKeyFrame();
                                kfR.KeyTime = ts;
                                kfR.Value = ep.Angle;
                                daR.KeyFrames.Add(kfR);
                            }
                        }
                        _lastPoint = p;
                    }
                }
            }

            element.Resources.Add("MyAnimation", sb);

            sb.Begin();
            return;
        }

        private void StopDrawing()
        {
            _isDrawing = false;
            _lastPoint = null;
            OnPathDrawn();
        }

        private void AddPoint(Point point)
        {
            _pathFigure = new PathFigure();

            if (_lastPoint != null)
            {
                //_polyLineSegment.Points.Add(_lastPoint.Value);
                _pathFigure.StartPoint = _lastPoint.Value;
            }

            _pathGeometry.Figures.Add(_pathFigure);

            _polyLineSegment = new PolyLineSegment();

            _polyLineSegment.Points.Add(point);
            _pathFigure.Segments.Add(_polyLineSegment);

            //PolyLineSegment pls = new PolyLineSegment();
            //pls.p
            //_pathFigure.Segments.Add(_polyLineSegment);

            _lastPoint = point;
        }

    }

    public class PathDrawnEventArgs : EventArgs
    {
        public Path Path { get; set; }
        public FrameworkElement Parent { get; set; }
    }
}
